/**
 * \file SGrille.c
 * \author Catalin BLAJ
 * \author Vincent JACQUEMIN
 * \date 19 Octobre 2013
 *
 * Fichier source SGrille.c pour le programme du Sudoku
 */


#include "SGrille.h"

SGrille* SG_initialiser(unsigned char type)
{
    /* Variables de convenance */
    unsigned short nombreUnites = type*type;
    unsigned nombreCases = nombreUnites*nombreUnites;

    /* Vérification des paramètres */
    assert(type>=2 && type<=4);

    /* Allocation mémoire et affectation des champs de structure */
    SGrille* grille = (SGrille*) malloc(sizeof(SGrille));
    grille->type = type;
    grille->cases = (SCase**) malloc(nombreCases*sizeof(SCase*));

    /* On alloue toutes les cases */
    for (unsigned i=0; i<nombreCases; i++)
        grille->cases[i] = SC_initialiser(type, i);

	/* On alloue toutes les lignes */
    grille->lignes = (SUnite**) malloc(nombreUnites*sizeof(SUnite*));
    for (unsigned i=0; i<nombreUnites; i++)
    {
        /* On crée un tableau de SUnite dans chaque case du tableau des lignes */
		grille->lignes[i] = SU_initialiser(type);
        grille->lignes[i]->indice = i;
        /* On met chaque case du tableau dans les bonnes unités */
        for (unsigned j=0; j<nombreUnites; j++)
            SU_setCase(SG_getLigne(grille, i), j, SG_getCase(grille, i*nombreUnites+j));
    }

    /* On alloue toutes les colonnes */
    grille->colonnes = (SUnite**) malloc(nombreUnites*sizeof(SUnite*));
    for (unsigned i=0; i<nombreUnites; i++)
    {
        /* On crée un tableau de SUnite dans chaque case du tableau des colonnes */
		grille->colonnes[i] = SU_initialiser(type);
        grille->colonnes[i]->indice = i;
        for(unsigned j=0; j<nombreUnites; j++)
            SU_setCase(SG_getColonne(grille, i), j, SG_getCase(grille, j*nombreUnites+i));
    }

    /* On alloue toutes les régions */
    grille->regions = (SUnite**) malloc(nombreUnites*sizeof(SUnite*));
    for (unsigned i=0; i<nombreUnites; i++)
    {
        /* On crée un tableau de SUnite dans chaque case du tableau des régions */
		grille->regions[i] = SU_initialiser(type);
        grille->regions[i]->indice = i;
        for(unsigned j=0; j<nombreUnites; j++)
        {
            unsigned indiceCase = (((i/type)*type)+(j/type))*nombreUnites + (((i%type)*type)+(j%type));
            SU_setCase(SG_getRegion(grille, i), j, SG_getCase(grille, indiceCase));
        }
    }

    /* On retourne la grille correctement allouée */
    return grille;
}


SGrille* SG_initialiserCopie(const SGrille* grille)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned nombreCases = type*type*type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);

    /* On déclare une nouvelle grille et on initialise tous les champs avec ceux de la grille originale */
    SGrille* grilleCopie = SG_initialiser(type);
    assert(grille != NULL);
    for(unsigned i=0; i<nombreCases; i++)
    {
        SC_setEtat(SG_getCase(grilleCopie, i), SC_getEtat(SG_getCase(grille, i)));
        SC_setValeur(SG_getCase(grilleCopie, i), SC_getValeur(SG_getCase(grille, i)));
        SC_setCandidats(SG_getCase(grilleCopie, i), SC_getTousCandidats(SG_getCase(grille, i)));
    }

    /* On retourne la grille correctement allouée */
    return grilleCopie;
}

SGrille* SG_initialiserChargement(const char* chaine)
{
    /* Variable de convenance */
    unsigned char type = chaine[0]-48; /* On récupère la vraie valeur du char en enlevant 48 (car 0 est à la place 48 dans la table ASCII) */

    /* Vérification des paramètres */
    assert(chaine != NULL);

    /* Variables */
    unsigned taille = strlen(chaine);
    unsigned indice = 0;
    char sousChaine[9] = ""; /* Sous-chaine qui stockera chaque chaine de cases */
    SCase* maCase = SC_initialiser(type, indice);

    /* On crée la grille à renvoyer */
    SGrille* grille = SG_initialiser(type);
    assert(grille != NULL);
    for(unsigned i=1; i<taille; i++)
    {
        /* Si le caractère est un '|', la sous-chaine contient une chaine de case */
        if (chaine[i] == '|')
        {
            /* On charge la case dans la grille */
            SC_chargement(maCase, sousChaine);
            SG_setCase(grille, indice, maCase);

            indice++;
            sousChaine[0] = '\0'; /* on remet la sous-chaine à vide */
        }
        else
            ajouterCaractere(sousChaine, chaine[i]);
    }

    free(maCase);
	return grille;
}

void SG_testament(SGrille* grille)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned short nombreUnite =type*type;
    unsigned nombreCases = nombreUnite*nombreUnite;

    /* Vérification des paramètres */
    assert(grille != NULL);

    /* Libération de la mémoire des cases */
    for (unsigned i=0; i<nombreCases; i++)
        SC_testament(SG_getCase(grille, i));

    /* Libération de la mémoire des unités */
    for (unsigned i=0; i<nombreUnite; i++)
    {
        SU_testament(SG_getColonne(grille, i));
        SU_testament(SG_getLigne(grille, i));
        SU_testament(SG_getRegion(grille, i));
    }

    /* Libération des champs de la structure grille */
    free(grille->cases);
    free(grille->lignes);
    free(grille->colonnes);
    free(grille->regions);
    free(grille);
}

unsigned char SG_getType(const SGrille* grille)
{
    /* Vérification des paramètres */
    assert(grille != NULL);

    /* On renvoit le type de Sudoku */
    return grille->type;
}

SCase* SG_getCase(const SGrille* grille, unsigned indiceCase)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned short nombreCases = type*type*type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);
	assert(indiceCase < nombreCases);

    /* On renvoit la case cherchée */
    return grille->cases[indiceCase];
}


SUnite* SG_getLigne(const SGrille* grille, unsigned short indiceLigne)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned short nombreUnites = type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);
	assert(indiceLigne < nombreUnites);

    /* On renvoit la ligne */
    return grille->lignes[indiceLigne];
}


SUnite* SG_getColonne(const SGrille* grille, unsigned short indiceColonne)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned short nombreUnites = type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);
	assert(indiceColonne < nombreUnites);

    /* On renvoit la colonne */
    return grille->colonnes[indiceColonne];
}


SUnite* SG_getRegion(const SGrille* grille, unsigned short indiceRegion)
{
    /* Variables de convenances */
    unsigned char type = SG_getType(grille);
    unsigned short nombreUnites = type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);
	assert(indiceRegion < nombreUnites);

    /* On renvoit l'indice de la region */
    return grille->regions[indiceRegion];
}


void SG_setCase(SGrille* grille, unsigned short indice, SCase* nouvelleCase)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
	unsigned short nombreCases = type*type*type*type;

    /* Vérification des paramètres */
	assert(grille != NULL);
	assert(nouvelleCase != NULL);
	assert(indice < nombreCases);

	/* On ajoute la case dans la grille */
	SC_setValeur(grille->cases[indice], SC_getValeur(nouvelleCase));
	SC_setEtat(grille->cases[indice], SC_getEtat(nouvelleCase));
	SC_setCandidats(grille->cases[indice], SC_getTousCandidats(nouvelleCase));
}


void SG_setLigne(SGrille* grille, unsigned short indiceLigne, SUnite* nouvelleLigne)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned short nombreUnites = type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);
    assert(nouvelleLigne != NULL);
	assert(indiceLigne < nombreUnites);

    /* On ajoute la ligne dans la grille */
    grille->lignes[indiceLigne] = nouvelleLigne;
}


void SG_setColonne(SGrille* grille, unsigned short indiceColonne, SUnite* nouvelleColonne)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned short nombreUnites = type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);
    assert(nouvelleColonne != NULL);
	assert(indiceColonne < nombreUnites);

    /* On ajoute la ligne dans la grille */
    grille->colonnes[indiceColonne] = nouvelleColonne;
}


void SG_remplirGrilleAvecTableau4x4(SGrille* grille, int grilleTab[4][4])
{
    /* Vérification des paramètres */
    assert(grille != NULL);

    /* Double boucle pour initialiser les cases */
    unsigned nombreUnite = 4;
    for(unsigned l=0; l<nombreUnite; l++)
    {
        for(unsigned c=0; c<nombreUnite; c++)
        {
            if ((grilleTab[l][c] >= 0) && ((unsigned) grilleTab[l][c] < nombreUnite))
            {
                SC_setEtat(SU_getCase(SG_getColonne(grille, c), l), gele);
                SC_setCandidats(SU_getCase(SG_getColonne(grille, c), l), pow2(grilleTab[l][c]));
                SC_setValeur(SU_getCase(SG_getColonne(grille, c), l), grilleTab[l][c]);
            }
            else
            {
                SC_setEtat(SU_getCase(SG_getColonne(grille, c), l), vide);
                SC_setCandidats(SU_getCase(SG_getColonne(grille, c), l), 0);
            }
        }
    }
}

void SG_remplirGrilleAvecTableau9x9(SGrille* grille, int grilleTab[9][9])
{
    /* Vérification des paramètres */
    assert(grille != NULL);

    /* Double boucle pour initialiser les cases */
    unsigned nombreUnite = 9;
    for(unsigned l=0; l<nombreUnite; l++)
    {
        for(unsigned c=0; c<nombreUnite; c++)
        {
            if ((grilleTab[l][c] >= 0) && ((unsigned) grilleTab[l][c] < nombreUnite))
            {
                SC_setEtat(SU_getCase(SG_getColonne(grille, c), l), gele);
                SC_setCandidats(SU_getCase(SG_getColonne(grille, c), l), pow2(grilleTab[l][c]));
                SC_setValeur(SU_getCase(SG_getColonne(grille, c), l), grilleTab[l][c]);
            }
            else
            {
                SC_setEtat(SU_getCase(SG_getColonne(grille, c), l), vide);
                SC_setCandidats(SU_getCase(SG_getColonne(grille, c), l), 0);
                SC_setValeur(SU_getCase(SG_getColonne(grille, c), l), -1);
            }
        }
    }
}

void SG_remplirGrilleAvecTableau16x16(SGrille* grille, int grilleTab[16][16])
{
    /* Vérification des paramètres */
    assert(grille != NULL);

    /* Double boucle pour initialiser les cases */
    unsigned nombreUnite = 16;
    for(unsigned l=0; l<nombreUnite; l++)
    {
        for(unsigned c=0; c<nombreUnite; c++)
        {
            if ((grilleTab[l][c] >= 0) && ((unsigned) grilleTab[l][c] < nombreUnite))
            {
                SC_setEtat(SU_getCase(SG_getColonne(grille, c), l), gele);
                SC_setCandidats(SU_getCase(SG_getColonne(grille, c), l), pow2(grilleTab[l][c]));
                SC_setValeur(SU_getCase(SG_getColonne(grille, c), l), grilleTab[l][c]);
            }
            else
            {
                SC_setEtat(SU_getCase(SG_getColonne(grille, c), l), vide);
                SC_setCandidats(SU_getCase(SG_getColonne(grille, c), l), 0);
            }
        }
    }
}

char* SG_toChaine(const SGrille* grille)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned nombreCases = type*type*type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);

    /* Déclaration de variable  */
    char separateur[2] = "|";
    char* sauvegarde = malloc(TAILLE_SAUVEGARDE_MAX*sizeof(char));

    /* On commence par mettre le type dans la chaine */
    sprintf(sauvegarde, "%d", SG_getType(grille));

    /* On boucle sur les cases, on met les chaines formatées de chaque cases dans la chaine en séparant par '|' */
    for(unsigned i=0; i<nombreCases; i++)
    {
        char* chaineCase = SC_toChaine(SG_getCase(grille, i));
        strcat(sauvegarde, chaineCase);
        strcat(sauvegarde, separateur);
        free(chaineCase);
    }

    /* On renvoie la chaine de sauvegarde */
    return sauvegarde;
}
